package com.ctrip.cap.appium;

import io.appium.java_client.AppiumDriver;

import java.lang.reflect.Method;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.testng.ITestContext;
import org.testng.ITestResult;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.BeforeSuite;
import org.testng.annotations.Listeners;
import org.testng.xml.XmlTest;

import com.ctrip.cap.AppiumContext;
import com.ctrip.cap.CapLogger;
import com.ctrip.cap.common.exception.CapException;
import com.ctrip.cap.device.Device;
import com.ctrip.cap.exception.AppiumDriverException;
import com.ctrip.cap.internal.appium.suite.AppiumSuiteListener;
import com.ctrip.cap.internal.domain.Context;
import com.ctrip.cap.internal.performance.Profilers;
import com.ctrip.cap.internal.suite.ReporterSuiteListener;
import com.ctrip.cap.internal.suite.SuiteManListener;
import com.ctrip.cap.internal.test.MethodFilterChain;
import com.ctrip.cap.lanucher.AppiumConfig;
import com.ctrip.cap.page.CapPage;
import com.ctrip.cap.reporter.CapReporter;

/**
 * for appium auto start test
 * 
 * <pre class="code">
 * public class SomeTest extends AppiumTest {
 * 
 * 	&#064;Test
 * 	public void testFlightPage() {
 * 		AppiumDriver driver = getDriver();
 * 		Device device = getDevice();
 * 		HomePage homePage = new HomePage(driver, device).get();
 * 		step("to flight page");
 * 		homePage.toFlightPage();
 * 		assertElementPresent(By.linkText("查询"), driver, 6);
 * 	}
 * }
 * 
 * <pre/>
 * 
 * @author ltyao
 * 
 * @see CapPage better PageObject Practice
 * 
 */
@Listeners(value = { ReporterSuiteListener.class, AppiumSuiteListener.class,
		CapReporter.class })
public class AppiumTest {

	private static final Logger iLogger = LoggerFactory
			.getLogger(AppiumTest.class);

	private static final ThreadLocal<Context> appiumContextHolder = new InheritableThreadLocal<>();

	private static final ThreadLocal<Throwable> exceptionHolder = new InheritableThreadLocal<>();

	@BeforeSuite
	public void setUpLogger() {
		iLogger.info("Order debug {}", Profilers.listenerOrder());
		CapLogger.setContext(appiumContextHolder);

	}

	@BeforeMethod(alwaysRun = true)
	public final void setUpDriver(ITestContext testContext,
			Object[] parameters, Method method, XmlTest xmlTest) {
	
		AppiumContext context = new AppiumContext();
		try {
			AppiumConfig appiumConfig = (AppiumConfig) testContext.getSuite()
					.getAttribute(SuiteManListener.CAP_CONFIG);
			AppiumConfig config = appiumConfig.clone();

			context.setAppiumConfig(config);
			context.setTestContext(testContext);
			context.setXmlTest(xmlTest);
			context.setMethod(method);
			appiumContextHolder.set(context);

			MethodFilterChain.beforeMethod(context);

		} catch (Exception e) {
			exceptionHolder.set(e);
			throw new CapException("AppiumTest Set up Driver", e);
		} finally {
			// context.closeQuietly();
		}

		iLogger.info("setup {} down", method.getName());

	}

	@AfterMethod(alwaysRun = true)
	public void destroyDriver(ITestContext testContext, ITestResult testResult,
			Method method) {
		iLogger.info("after destroy start {}", method.getName());

		AppiumContext context = (AppiumContext) appiumContextHolder.get();
		if (context == null) {
			iLogger.warn("destroy driver abnormally");
			return;
		}

		try {
			context.setTestResult(testResult);
			MethodFilterChain.afterMethod(context);

			if (testResult.getStatus() == ITestResult.SKIP) {
				String hint = "New AppiumDriver(appium log="
						+ context.getAppium().getAppiumLog() + ") failure";
				Throwable throwable = exceptionHolder.get();

				if (throwable != null)
					testResult.setThrowable(new AppiumDriverException(hint,
							throwable));
			}

		} catch (Exception t) {
			iLogger.warn("after ", t);
		} finally {
			context.closeQuietly();
			appiumContextHolder.remove();
			exceptionHolder.remove();
		}

		iLogger.info("after destroy down {}", method.getName());

	}

	protected AppiumDriver getDriver() {
		AppiumContext context = (AppiumContext) appiumContextHolder.get();
		return context.getDriver();
	}

	protected Device getDevice() {
		AppiumContext context = (AppiumContext) appiumContextHolder.get();
		return context.getDevice();
	}

	protected Context getContext() {
		AppiumContext context = (AppiumContext) appiumContextHolder.get();
		return context;
	}

}
